package com.yuanli.card.authentication.config;

import com.yuanli.card.authentication.core.cache.ICacheManager;
import com.yuanli.card.authentication.core.cache.RedisCacheManager;
import com.yuanli.card.authentication.core.credentials.RetryLimitCredentialsMatcher;
import com.yuanli.card.authentication.core.filter.MRolesAuthorizationFilter;
import com.yuanli.card.authentication.core.realm.ShiroRealm;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.session.mgt.WebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Created by layne on 2018/5/30.
 */
@Configuration
public class ShiroAutoConfig {
    @Bean
    public ICacheManager cacheManager(RedisTemplate redisTemplate) {
        return new RedisCacheManager(redisTemplate);
    }

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
        daap.setProxyTargetClass(true);
        return daap;
    }

    @Bean
    public RetryLimitCredentialsMatcher retryLimitHashedCredentialsMatcher(ICacheManager cacheManager) {
        RetryLimitCredentialsMatcher matcher =
                new RetryLimitCredentialsMatcher(cacheManager.getCache("password_retry_cache",
                        600000L));
        matcher.setHashAlgorithmName("md5");
        matcher.setHashIterations(2);
        matcher.setStoredCredentialsHexEncoded(true);
        return matcher;
    }

    @Bean
    public ShiroRealm shiroRealm(ICacheManager cacheManager) {
        ShiroRealm realm = new ShiroRealm();
        realm.setCredentialsMatcher(retryLimitHashedCredentialsMatcher(cacheManager));
        realm.setCacheManager(cacheManager);
        return realm;
    }

    /**
     * 会话调度验证器
     * 全局的会话信息检测扫描信息间隔30分钟
     *
     * @return
     */
    @Bean
    public QuartzSessionValidationScheduler sessionValidationScheduler() {
        QuartzSessionValidationScheduler sessionValidationScheduler = new QuartzSessionValidationScheduler();
        sessionValidationScheduler.setSessionValidationInterval(1440000);
        return sessionValidationScheduler;
    }

    /**
     * 会话DAO
     *
     * @return
     */
    @Bean
    public EnterpriseCacheSessionDAO sessionDAO(ICacheManager cacheManager) {
        EnterpriseCacheSessionDAO sessionDAO = new EnterpriseCacheSessionDAO();
        sessionDAO.setSessionIdGenerator(new JavaUuidSessionIdGenerator());
        sessionDAO.setActiveSessionsCache(cacheManager.getCache("active_session_cache", 600000L));
        return sessionDAO;
    }

    /**
     * 会话管理器
     * 全局的会话信息设置成12小时,sessionValidationSchedulerEnabled参数就是是否开启扫描
     *
     * @param sessionValidationScheduler
     * @param sessionDAO
     * @return
     */
    @Bean
    public WebSessionManager sessionManager(ICacheManager cacheManager) {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setGlobalSessionTimeout(43200000);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionValidationSchedulerEnabled(true);
        QuartzSessionValidationScheduler sessionValidationScheduler = sessionValidationScheduler();
        sessionValidationScheduler.setSessionManager(sessionManager);
        sessionManager.setSessionValidationScheduler(sessionValidationScheduler);
        sessionManager.setSessionDAO(sessionDAO(cacheManager));
        return sessionManager;
    }

    @Bean
    public DefaultWebSecurityManager securityManager(ICacheManager cacheManager) {
        DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
        //添加授权信息
        dwsm.setRealm(shiroRealm(cacheManager));
        dwsm.setSessionManager(sessionManager(cacheManager));
        dwsm.setCacheManager(cacheManager);
        return dwsm;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(ICacheManager cacheManager) {
        AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
        aasa.setSecurityManager(securityManager(cacheManager));
        return aasa;
    }

    /**
     * @param shiroFilterFactoryBean
     */
    private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean) {
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/user/login", "anon");
        filterChainDefinitionMap.put("/logout", "anon");
        filterChainDefinitionMap.put("/**", "anon");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    }

    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(ICacheManager cacheManager) {
        ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
        filterFactoryBean.setSecurityManager(securityManager(cacheManager));
        Map<String, Filter> filters = new HashMap<>();
        filters.put("mroles", new MRolesAuthorizationFilter());
        filterFactoryBean.setFilters(filters);
        filterFactoryBean.setLoginUrl("/405");
        filterFactoryBean.setUnauthorizedUrl("/403");
        loadShiroFilterChain(filterFactoryBean);
        return filterFactoryBean;
    }
}